/***************************************************************************
 *   oboe.math.Number3D                                                    *
 *   Copyright (C) 2011 by Felipe Manga                                    *
 *   lufeboma@gmail.com                                                    *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

package oboe.math
{
	import flash.utils.ByteArray;

	/**
	 * A class for facilitating math with 3D vectors.. 
	 * @author FelipeManga
	 * 
	 */
	public class Number3D
	{
		/**
		 * The X (width) component 
		 */
		public var x : Number;
		
		/**
		 * The Y (height) component 
		 */
		public var y : Number;
		
		/**
		 * The Z (depth) component 
		 */
		public var z : Number;
		
		/**
		 * Class constructor for initializing x/y/z 
		 * @param x
		 * @param y
		 * @param z
		 * 
		 */
		public function Number3D( x : Number, y : Number, z : Number )
		{
			this.x = x;
			this.y = y;
			this.z = z;
		}
		
		/**
		 * Clone function provided for compatibility with code written for Away3D. 
		 * @param n The Number3D to read data from.
		 * 
		 */
		public function clone( n : Number3D ) : void
		{
			this.x = n.x;
			this.y = n.y;
			this.z = n.z;
		}
		
		/**
		 * Tests if this == n 
		 * @param n the number to test equality with
		 * @return true if this object's components have the same value as n's.
		 * 
		 */
		public function isEqual( n : Number3D ) : Boolean
		{
			return this.x == n.x && this.y == n.y && this.z == n.z;
		}
		
		public function isEqualEx( n:Number3D, precision:Number ):Boolean
		{
			return int(this.x*precision)==int(n.x*precision) &&
				int(this.y*precision)==int(n.y*precision) &&
				int(this.z*precision)==int(n.z*precision);
		}
		
		/**
		 * Converts this object into a json-style string. 
		 * @return the x/y/z components in a string. Example: "{ x:0, y:1, z:0 }"
		 * 
		 */
		public function toString() : String 
		{
			return "{ x: " + x + ", y: " + y + ", z: " + z + " }"; 
		}
		
		
		/**
		 * Reads the 3 components as floats from a ByteArray. 
		 * @param ba the byte array
		 * @return this
		 * 
		 */
		public function readFloat( ba : ByteArray ) : Number3D 
		{
			x = ba.readFloat();
			y = ba.readFloat();
			z = ba.readFloat();
			return this;
		}
		
		/**
		 * Reads the 3 components as unsigned bytes from a ByteArray. 
		 * @param ba the byte array
		 * @return this
		 * 
		 */
		public function readByte( ba : ByteArray ) : Number3D 
		{
			x = ba.readByte() & 0xFF;
			y = ba.readByte() & 0xFF;
			z = ba.readByte() & 0xFF;
			return this;
		}		
		
		public function mod ( o:Number ):Number3D
		{
			if( o<0 ) o=-o;
			
			var t:Number = int(x/o);
			if( x<0 ) x += o;
			else x-=0;
			
			t = int(y/o);
			if( y<0 ) y += o;
			else y-=0;

			t = int(z/o);
			if( z<0 ) z += o;
			else z-=0;
			
			return this;
		}
		
// direct port from prerenderer source code
		/**
		 * multiplies each of this object's components by a certain amount
		 * @param o the amount to multiply by
		 * @return this
		 * 
		 */
		public function mul ( o : Number ) : Number3D 
		{
			x *= o;
			y *= o;
			z *= o;
			return this;
		}
		
		/**
		 * Multiplies this vector by another, overwriting it's own components. 
		 * @param o The vector to multiply by.
		 * @return this
		 * 
		 */
		public function mul3 ( o : Number3D ) : Number3D 
		{
			x *= o.x;
			y *= o.y;
			z *= o.z;
			return this;
		}
		
		/**
		 * Multiplies this object's x/y/z components by the parameters and adds them together (does not modify x/y/z).  
		 * @param a the value to multiply X by
		 * @param b the value to multiply Y by
		 * @param c the value to multiply Z by
		 * @return the sum of the components multiplied by a, b, and c.
		 * 
		 */
		public function mulsum2x ( a : Number, b : Number, c : Number ) : Number 
		{
			return x*a + y*b + z*c;
		}
		
		/**
		 * Multiplies this object's x/y/z components by a vector and adds them together (does not modify x/y/z).  
		 * @param o the vector to multiply by
		 * @return the sum of the components multiplied by the passed vector.
		 * 
		 */
		public function mulsum3 ( o : Number3D ) : Number 
		{
			return x*o.x + y*o.y + z*o.z;
		}
			
		public function div ( o : Number ) : Number3D 
		{
			x /= o;
			y /= o;
			z /= o;
			return this;
		}
		public function div3 ( o : Number3D ) : Number3D {
			x /= o.x;
			y /= o.y;
			z /= o.z;
			return this;
		}
				
		public function add ( o : Number ) : Number3D {
			x += o;
			y += o;
			z += o;
			return this;
		}
		public function add3 ( o : Number3D ) : Number3D {
			x += o.x;
			y += o.y;
			z += o.z;
			return this;
		}
						
		public function sub3 ( o : Number3D ) : Number3D {
			x -= o.x;
			y -= o.y;
			z -= o.z;
			return this;
		}
							
		public function copy3( o : Number3D ) : Number3D {
			x = o.x;
			y = o.y;
			z = o.z;
			return this;
		}
		
		public function copy2( x : Number, y : Number, z : Number ) : Number3D {
			this.x = x;
			this.y = y;
			this.z = z;
			return this;
		}

		public function copy( o : Number ) : Number3D {
			x = o;
			y = o;
			z = o;
			return this;
		}
		
		public function flipYZ() : Number3D {
			var t : Number = y;
			y = z;
			z = t;
			return this;
		}

		public function flipXY() : Number3D {
			var t : Number = y;
			y = x;
			x = t;
			return this;
		}

		public function flipXZ() : Number3D {
			var t : Number = x;
			x = z;
			z = t;
			return this;
		}
		
		public function cross( v1 : Number3D, v2 : Number3D ) : Number3D 
		{
			x=v1.y*v2.z-v1.z*v2.y;
			y=v1.z*v2.x-v1.x*v2.z;
			z=v1.x*v2.y-v1.y*v2.x;
			return this;
		}
		
		public function lerp( v1 : Number3D, v2 : Number3D, f:Number ): Number3D
		{
			x = v1.x*(1-f) + v2.x*f;
			y = v1.y*(1-f) + v2.y*f;
			z = v1.z*(1-f) + v2.z*f;
			return this;
		}
		
		public function dotProduct( v2 : Number3D ) : Number
		{
			// return mulsum3( v2 );
			return x*v2.x + y*v2.y + z*v2.z;
		}
		
		public function length() : Number {
			return Math.sqrt( x*x+y*y+z*z );
		}

		public function lengthSquared() : Number {
			return x*x+y*y+z*z;
		}
		
		public function normalize() : Number3D 
		{
			var d : Number = this.length();
			if( d>0 ){
				x/=d;
				y/=d;
				z/=d;
			}else{
				x=y=z=0;
			}
			return this;
		}
		
		public function floor():Number3D
		{
			x=int(x);
			y=int(y);
			z=int(z);
			return this;
		}

		public function setRandom( min : Number, max : Number ) : Number3D 
		{
			x = min + Math.random()*(max-min);
			y = min + Math.random()*(max-min);
			z = min + Math.random()*(max-min);
			return this;
		}
	}
}